package dragon.modules.system.dic.dao;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import dragon.core.db.DBUtil;
import dragon.core.db.DML;
import dragon.core.exception.ExceptionHandler;
import dragon.core.system.po.SysDic;
import dragon.core.util.GlobalParameter;
import dragon.core.util.Reflect;
import ejp.Database;

public class DicDaoImpl {
	Logger logger = LoggerFactory.getLogger(DicDaoImpl.class);

	/**
	 * 修父节点路径修复方法
	 * 
	 * @param id
	 * @param target
	 * @throws ExceptionHandler
	 */
	public void changeParent(int id, int target) throws ExceptionHandler {
		// 获取字典
		SysDic dic = getDic(id, true);

		// 加载新的父节点数据
		SysDic newParent = getDic(target, true);
		if (newParent == null) {
			newParent = new SysDic();
			newParent.setId(0);
			newParent.setDepth(1);
			newParent.setIdpath("/");
			newParent.setCodepath("/");
			newParent.setNamepath("/");
		}

		// 深度差
		int depth_odds = newParent.getDepth() - dic.getDepth() + 1;

		Database db = DBUtil.getDatabase();
		DML.beginTransaction(db);
		DML.executeUpdate(db, "update " + SysDic.tableName + " set pid=" + newParent.getId() + " where id=" + id);

		if (dic.getPid() == 0) {
			// 删除路径最后一个斜线，避免路径相加出现双斜线
			StringBuffer sb_idpath = new StringBuffer(newParent.getIdpath());
			newParent.setIdpath(sb_idpath.deleteCharAt(sb_idpath.lastIndexOf("/")).toString());
			StringBuffer sb_namepath = new StringBuffer(newParent.getNamepath());
			newParent.setNamepath(sb_namepath.deleteCharAt(sb_namepath.lastIndexOf("/")).toString());
			StringBuffer sb_codepath = new StringBuffer(newParent.getCodepath());
			newParent.setCodepath(sb_codepath.deleteCharAt(sb_codepath.lastIndexOf("/")).toString());

			DML.executeUpdate(db,
					"update " + SysDic.tableName + " set idpath='" + newParent.getIdpath() + "'||idpath,codepath='" + newParent.getCodepath() + "||codepath,namepath='" + newParent.getNamepath()
							+ "'||namepath , depth=depth+" + depth_odds + " where id=" + id + " or idpath like '%/" + id + "/%'");
		} else {
			// 加载旧的父节点数据
			SysDic oldParent = getDic(dic.getPid(), true);
			DML.executeUpdate(db,
					"update " + SysDic.tableName + " set idpath=replace(idpath,'" + oldParent.getIdpath() + "','" + newParent.getIdpath() + "'),codepath=replace(codepath,'" + oldParent.getCodepath()
							+ "','" + newParent.getCodepath() + "'),namepath=replace(namepath,'" + oldParent.getNamepath() + "','" + newParent.getNamepath() + "'), depth=depth+" + depth_odds
							+ "  where id=" + id + " or idpath like '%/" + id + "/%'");
			oldParent = null;
		}
		DML.endTransaction(db);
		// 删除原父节点下缓存缓存
		removeMemoryDic(dic.getPid());
		db = null;

		newParent = null;
		dic = null;
	}

	/**
	 * 修改名称路径
	 * 
	 * @param db
	 *            数据库对象，保证一个事务中操作
	 * @param id
	 *            部门节点ID
	 * @param oldName
	 *            旧的名称
	 * @param newName
	 *            新的名称
	 * @throws ExceptionHandler
	 */
	public void namePathRepair(Database db, int id, String oldName, String newName) throws ExceptionHandler {
		DML.executeUpdate(db, "update " + SysDic.tableName + " set namepath=replace(namepath,'/" + oldName + "/','/" + newName + "/') where id=" + id + " or idpath like '%/" + id + "/%' ");
	}

	/**
	 * 修改编码路径
	 * 
	 * @param db
	 *            数据库对象，保证一个事务中操作
	 * @param id
	 *            部门节点ID
	 * @param oldCode
	 *            旧的编码
	 * @param newCode
	 *            新的编码
	 * @throws ExceptionHandler
	 */
	public void codePathRepair(Database db, int id, String oldCode, String newCode) throws ExceptionHandler {
		DML.executeUpdate(db, "update " + SysDic.tableName + " set codepath=replace(codepath,'/" + oldCode + "/','/" + newCode + "/') where id=" + id + " or idpath like '%/" + id + "/%' ");
	}

	/**
	 * 从缓存中获取数据字典
	 * 
	 * @param id
	 *            字典ID
	 * @return
	 */
	private SysDic getMemoryDic(int id) {
		if (logger.isDebugEnabled()) {
			logger.debug("从缓存中获取数据字典：id=" + id + " dic=" + (GlobalParameter.Dics.get(String.valueOf(id)) != null ? GlobalParameter.Dics.get(String.valueOf(id)).toString() : "not exist"));
		}
		return GlobalParameter.Dics.get(String.valueOf(id));
	}

	/**
	 * 缓存数据字典
	 * 
	 * @param id
	 *            字典ID
	 * @param dic
	 *            数据字典
	 */
	private void addMemoryDic(int id, SysDic dic) {
		if (logger.isDebugEnabled()) {
			logger.debug("向缓存中加入数据字典：id=" + id + " dic=" + dic.toString());
		}
		GlobalParameter.Dics.put(String.valueOf(id), dic);
	}

	/**
	 * 移除数据字典
	 * 
	 * @param id
	 *            字典ID
	 */
	private void removeMemoryDic(int id) {
		if (logger.isDebugEnabled()) {
			logger.debug("从缓存中移除数据字典：id=" + id);
		}
		GlobalParameter.Dics.remove(String.valueOf(id));

		// 同时移除子级节点数据
		removeMemoryDicChildren(id);
	}

	/**
	 * 从缓存中获取子级数据字典
	 * 
	 * @param pid
	 *            父节点ID
	 * @return
	 */
	private List<SysDic> getMemoryDicChildren(int pid) {
		if (logger.isDebugEnabled()) {
			logger.debug("从缓存中获取数据字典：pid=" + pid + "  children=" + (GlobalParameter.DicChildren.get(String.valueOf(pid)) != null ? GlobalParameter.DicChildren.get(String.valueOf(pid)).size() : -1));
		}
		return GlobalParameter.DicChildren.get(String.valueOf(pid));
	}

	/**
	 * 缓存子级数据字典
	 * 
	 * @param pid
	 *            父节点ID
	 * @param children
	 *            子级数据字典
	 */
	private void addMemoryDicChildren(int pid, List<SysDic> children) {
		if (logger.isDebugEnabled()) {
			logger.debug("向缓存中增加数据字典：pid=" + pid + " children=" + children.size());
		}
		GlobalParameter.DicChildren.put(String.valueOf(pid), children);
	}

	/**
	 * 移除子级数据字典
	 * 
	 * @param pid
	 *            父节点ID
	 */
	private void removeMemoryDicChildren(int pid) {
		if (logger.isDebugEnabled()) {
			logger.debug("从缓存中移除数据字典：pid=" + pid);
		}
		GlobalParameter.DicChildren.remove(String.valueOf(pid));
	}

	/**
	 * 获取指定节点下的所有直接子节点
	 * 
	 * @param PID
	 *            父id
	 * @param includeStopData
	 *            为true时包含停用的数据，为false时不包含停用的数据
	 * @return
	 */
	public List<SysDic> getDics(int PID, boolean includeStopData) throws ExceptionHandler {

		// Step 1.尝试从缓存中获取
		List<SysDic> memoryData = getMemoryDicChildren(PID);
		if (memoryData != null) {
			return memoryData;
		}

		// Step 2-1.查询数据库
		List<SysDic> list = new ArrayList<SysDic>();
		String sql = "select * from " + SysDic.tableName + " where 1=1";
		if (!includeStopData) {
			sql += " and state='1'";
		}
		sql += " and pid=" + PID + " order by depth,ordernum,id asc";
		List<Map<String, Object>> maps = DML.query(sql);
		for (Map<String, Object> map : maps) {
			SysDic sd = new SysDic();
			// sd = map2class(map, sd);
			Reflect.map2Obj(map, sd);
			list.add(sd);
		}

		// Step 2-2.缓存结果
		addMemoryDicChildren(PID, list);

		return list;
	}

	/**
	 * 更改字典状态
	 * 
	 * @param id
	 *            字典ID
	 * @param state
	 *            字典状态 0 停用；1启用
	 * @return
	 */
	public int changeDicState(int id, int state) throws ExceptionHandler {
		// 缓存数据处理
		SysDic dic = getDic(DBUtil.getDatabase(), id, true);
		removeMemoryDicChildren(dic.getPid());
		dic = null;
		removeMemoryDic(id);

		return DML.executeUpdate("update  " + SysDic.tableName + " set state= '" + state + "' where idpath like '%/" + id + "/%' or idpath like '%/" + id + "'");
	}

	/**
	 * 更改字典是否是叶子节点
	 * 
	 * @param db
	 *            连接的数据库
	 * @param id
	 *            字典ID
	 * @param leaf
	 *            是否叶子节点 0 否；1是
	 * @return
	 */
	public int changeIsLeaf(Database db, int id, String leaf) throws ExceptionHandler {
		// 缓存数据处理
		SysDic dic = getDic(db, id, true);
		removeMemoryDicChildren(dic.getPid());
		dic = null;
		removeMemoryDic(id);

		return DML.executeUpdate(db, "update  " + SysDic.tableName + " set isleaf = '" + leaf + "' where id=" + id);
	}

	/**
	 * 加载数据字典
	 * 
	 * @param db
	 *            数据库连接对象
	 * @param id
	 *            字典ID
	 * @param fromDB
	 *            数据来源，true为数据库，false为缓存
	 * @return
	 * @throws ExceptionHandler
	 */
	public SysDic getDic(Database db, int id, boolean fromDB) throws ExceptionHandler {

		if (id > 0) {
			if (fromDB) {
				SysDic d = DML.loadObject(db, SysDic.class, "where id=?", id);
				return d;
			} else {

				// Step 1.尝试从缓存中获取
				SysDic dic = getMemoryDic(id);
				if (dic != null) {
					return dic;
				}
				// Step 2-1.从数据库中获取
				dic = DML.loadObject(db, SysDic.class, "where id=?", id);

				// Step 2-2.缓存数据字典
				if (dic != null) {
					addMemoryDic(id, dic);
				}
				return dic;
			}
		} else {
			return new SysDic();
		}
	}

	/**
	 * 加载数据字典
	 * 
	 * @param id
	 *            字典ID
	 * @param fromDB
	 *            是否从数据库取
	 * @return
	 * @throws ExceptionHandler
	 */
	public SysDic getDic(int id, boolean fromDB) throws ExceptionHandler {

		return getDic(DBUtil.getDatabase(), id, fromDB);
	}

}
